/*
	PlAr is Platform Arena: a 2D multiplayer shooting game
	Copyright (c) 2010, Antonio Ragagnin <spocchio@gmail.com>
    All rights reserved.

    This file is licensed under the New BSD License.
*/

/**
 * {@link Level} manage a set of classes {@link Element},
 * interface these with the JBox2D {@link World}, and manage the collisions and the creations of the {@link Element}.
 *
 * @author Antonio Ragagnin
 *
 */
 
package plar.core;

import java.util.ArrayList;
import java.util.List;
import org.jbox2d.dynamics.Body;
import org.jbox2d.dynamics.BodyDef;
import org.jbox2d.dynamics.BodyType;
import org.jbox2d.dynamics.Fixture;
import org.jbox2d.dynamics.FixtureDef;
import org.jbox2d.callbacks.*;
import org.jbox2d.dynamics.World;

import org.jbox2d.common.*;
import org.jbox2d.dynamics.contacts.*;
import org.jbox2d.dynamics.joints.Joint;

import org.jbox2d.collision.*;
import org.jbox2d.collision.shapes.PolygonShape;


public class Level implements Runnable {

	/**
	* the JBox2D world
	*/
    public World world;

	/**
	* the queue of {@link Player}s that will be recreated as soon as possible
	*/
    public List <ElementPlayer> respawnQueue;
	
	/**
	* the queue of {@link Element}s that have to be processed to be destroyed.
	*/
    public List<Element> destroyQueue;
	/**
	* fraction of time to evolve the World for each step.
	* the evolution is managed by {@link Game}, but it is used by frames();
	*/
    public float Dt = 0.01f;
	
	/**
	* the size of the Level
	*/
    public Vec2 dim;
	
	/**
	* description of the level
	*/
    public String description;
	
	/**
	* gravity acceleration
	*/
    public Vec2 g;
	
	/**
	* counter of the elements inside the level, do not modify.
	*/
    public int n=0;
	
	
    public Level() {
        respawnQueue = new ArrayList<ElementPlayer> ();
        destroyQueue = new ArrayList<Element> ();
        dim = new Vec2(100, 60);
        g = new Vec2(0, 10f);
        description = "Level";
    }
	
	/**
	* initialize the JBox2D features. 
	*/

    public void Initialize() {

        world.setDestructionListener(new DestructionListener() {

            @Override
            public void sayGoodbye(Joint joint) {


            }

            @Override
            public void sayGoodbye(Fixture fixture) {
                Object o = fixture.getBody().getUserData();
                Common.info(2, "JBox2D.sayGoodbye() " + o);

            }
        });
        world.setContactListener(new ContactListener() {

            private void setCollision(Element e1, Element e2) {
                if (e1.isRunnable) {
                    ArrayList<Element> cl1 = e1.collisionList;

                    if (cl1 == null)
                        cl1 = new ArrayList<Element>();
                    if (!cl1.contains(e2))
                        cl1.add(e2);
                    e1.collisionList = cl1;
                }

            }
            private void unsetCollision(Element e1, Element e2) {
                if (e1.isRunnable) {
                    ArrayList<Element> cl1 = e1.collisionList;

                    if (cl1 != null)
                        if (cl1.contains(e2))
                            cl1.remove(e2);
                    e1.collisionList = cl1;
                }

            }

            public void beginContact(Contact contact) {
                Contact point = contact;
                //point.flagForFiltering();

                if (point.isTouching()) {

                    // Common.info("collision at:"+point.position);
                    Element e1 = (Element) point.getFixtureA().getBody()
                                 .getUserData();
                    Element e2 = (Element) point.getFixtureB().getBody()
                                 .getUserData();

                    setCollision(e1, e2);
                    setCollision(e2, e1);
                    Common.info(4, "Collision detected: beetween " + e1 + " & "
                                + e2);

                }

            }

            public void endContact(Contact contact) {
                Contact point = contact;
                //	point.flagForFiltering();

                Element e1 = (Element) point.getFixtureA().getBody()
                             .getUserData();
                Element e2 = (Element) point.getFixtureB().getBody()
                             .getUserData();
                unsetCollision(e1, e2);
                unsetCollision(e2, e1);

                Common.info(4, "Collision finished from " + e1 + " and " + e2);
            }


            public void postSolve(Contact contact, ContactImpulse impulse) {
                contact.flagForFiltering();


            }


            public void preSolve(Contact contact, Manifold oldManifold) {


            }
        });


    }
	/**
	* add an element and initialize it
	*/
    public void addElement(Element e) {

        e.level = this;
        e.setBodyDef(e.bodydef);
        Body b = world.createBody(e.bodydef);
        e.body=b;
        e.setBody(b);
        n++;
        e.id=n;


        if (e.body == null)
            Common.info(1, "Level: new Body is null: " + e + " " + e.body);
    }
	/**
	* add an to the destroy queue.
	*/
    public void delElement(Element e) {

        //	Common.info(3, "Level.delElement(): deleting " + e.body.getUserData());
        //e.body.setUserData(null);
        destroyQueue.add(e);

    }

    public ArrayList<Element> getElementsByType(Common.ElementType s) {
        ArrayList<Element> l = new ArrayList<Element>();
        Body b = world.getBodyList();
        int i = 0;
        while (b != null) {
            i++;
            Element e = (Element) b.getUserData();
            if (e != null && e.type == s)
                l.add(e);
            b = b.getNext();
        }

        return l;
    }

    public ArrayList<ElementPlayer> getPlayers() {
         ArrayList<Element> al = getElementsByType(Common.ElementType.PLAYER);
		 ArrayList<ElementPlayer> ap = new ArrayList<ElementPlayer> ();
		 for(Element e:al)
		 {
		 ap.add((ElementPlayer)e);
		 }
		 return ap;

    }

    public ArrayList<Element> getRunnable() {
        ArrayList<Element> l = new ArrayList<Element>();
        Body b = world.getBodyList();
        int i = 0;
        while (b != null) {
            i++;
            Element e = (Element) b.getUserData();
            if (e != null && e.isRunnable)
                l.add(e);
            b = b.getNext();
        }

        return l;

    }

    public ArrayList<Element> getScreen(Vec2 a, Vec2 b) {
        AABB r = new AABB(new Vec2(a.x, a.y), new Vec2(a.x + b.x, a.y + b.y));
        ArrayList<Element> al = new ArrayList<Element>();
        ArrayList<Fixture> ss = Common.queryAABB(world, r);

        try {

            for (Fixture s : ss) {
                al.add((Element) s.getBody().getUserData());
            }
        } catch (Exception ex) {
            Common.info(1, "Level.getScreen() got exception:" + ex);
        }
        return al;
    }

    public void run() {

    }

    public int frames(float f) {
        return (int) (f / (Dt));
    }
    public void reSpawn(ElementPlayer p) {
        respawnQueue.add(p);

    }
    public String toString()
    {   return description;
    }
}
